//! View Entity相关的数据结构
//! 基于OFBiz party-entitymodel.xml中的view-entity定义

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// PartyAndPostalAddress view-entity
/// 参与方和邮政地址的组合视图
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartyAndPostalAddress {
    // Party fields
    pub party_id: String,
    pub party_type_id: String,
    pub status_id: Option<String>,
    
    // PartyContactMech fields
    pub contact_mech_id: String,
    pub from_date: DateTime<Utc>,
    pub thru_date: Option<DateTime<Utc>>,
    pub comments: Option<String>,
    pub extension: Option<String>,
    pub allow_solicitation: Option<bool>,
    
    // ContactMech fields
    pub contact_mech_type_id: String,
    pub info_string: Option<String>,
    
    // PostalAddress fields
    pub to_name: Option<String>,
    pub attn_name: Option<String>,
    pub address1: Option<String>,
    pub address2: Option<String>,
    pub house_number: Option<String>,
    pub house_number_ext: Option<String>,
    pub directions: Option<String>,
    pub city: Option<String>,
    pub city_geo_id: Option<String>,
    pub postal_code: Option<String>,
    pub postal_code_ext: Option<String>,
    pub country_geo_id: Option<String>,
    pub state_province_geo_id: Option<String>,
    pub county_geo_id: Option<String>,
    pub municipality_geo_id: Option<String>,
    pub postal_code_geo_id: Option<String>,
    pub geo_point_id: Option<String>,
}

/// PartyAndPerson view-entity
/// 参与方和个人信息的组合视图
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartyAndPerson {
    // Party fields
    pub party_id: String,
    pub party_type_id: String,
    pub external_id: Option<String>,
    pub preferred_currency_uom_id: Option<String>,
    pub description: Option<String>,
    pub status_id: Option<String>,
    pub created_date: Option<DateTime<Utc>>,
    pub created_by_user_login: Option<String>,
    pub last_modified_date: Option<DateTime<Utc>>,
    pub last_modified_by_user_login: Option<String>,
    pub data_source_id: Option<String>,
    pub is_unread: Option<bool>,
    
    // Person fields
    pub salutation: Option<String>,
    pub first_name: Option<String>,
    pub middle_name: Option<String>,
    pub last_name: Option<String>,
    pub personal_title: Option<String>,
    pub suffix: Option<String>,
    pub nickname: Option<String>,
    pub first_name_local: Option<String>,
    pub middle_name_local: Option<String>,
    pub last_name_local: Option<String>,
    pub other_local: Option<String>,
    pub member_id: Option<String>,
    pub gender: Option<String>,
    pub birth_date: Option<DateTime<Utc>>,
    pub deceased_date: Option<DateTime<Utc>>,
    pub height: Option<f64>,
    pub weight: Option<f64>,
    pub mothers_maiden_name: Option<String>,
    pub marital_status_enum_id: Option<String>,
    pub social_security_number: Option<String>,
    pub passport_number: Option<String>,
    pub passport_expire_date: Option<DateTime<Utc>>,
    pub total_years_work_experience: Option<f64>,
    pub comments: Option<String>,
    pub employment_status_enum_id: Option<String>,
    pub residence_status_enum_id: Option<String>,
    pub occupation: Option<String>,
    pub years_with_employer: Option<i32>,
    pub months_with_employer: Option<i32>,
    pub existing_customer: Option<bool>,
    pub card_id: Option<String>,
}

/// PartyAndGroup view-entity
/// 参与方和组织信息的组合视图
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartyAndGroup {
    // Party fields
    pub party_id: String,
    pub party_type_id: String,
    pub external_id: Option<String>,
    pub preferred_currency_uom_id: Option<String>,
    pub description: Option<String>,
    pub status_id: Option<String>,
    pub created_date: Option<DateTime<Utc>>,
    pub created_by_user_login: Option<String>,
    pub last_modified_date: Option<DateTime<Utc>>,
    pub last_modified_by_user_login: Option<String>,
    pub data_source_id: Option<String>,
    pub is_unread: Option<bool>,
    
    // PartyGroup fields
    pub group_name: Option<String>,
    pub group_name_local: Option<String>,
    pub office_site_name: Option<String>,
    pub annual_revenue: Option<f64>,
    pub num_employees: Option<i64>,
    pub ticker_symbol: Option<String>,
    pub group_comments: Option<String>,
    pub logo_image_url: Option<String>,
}

impl PartyAndGroup {
    /// 从HashMap创建PartyAndGroup实例
    pub fn from_data_row(data: &HashMap<String, String>) -> crate::Result<Self> {
        let party_id = data.get("partyId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing partyId".to_string())))?
            .clone();
        let party_type_id = data.get("partyTypeId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing partyTypeId".to_string())))?
            .clone();

        Ok(Self {
            party_id,
            party_type_id,
            external_id: data.get("externalId").cloned(),
            preferred_currency_uom_id: data.get("preferredCurrencyUomId").cloned(),
            description: data.get("description").cloned(),
            status_id: data.get("statusId").cloned(),
            created_date: data.get("createdDate")
                .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
                .map(|dt| dt.with_timezone(&Utc)),
            created_by_user_login: data.get("createdByUserLogin").cloned(),
            last_modified_date: data.get("lastModifiedDate")
                .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
                .map(|dt| dt.with_timezone(&Utc)),
            last_modified_by_user_login: data.get("lastModifiedByUserLogin").cloned(),
            data_source_id: data.get("dataSourceId").cloned(),
            is_unread: data.get("isUnread").map(|s| s == "Y"),
            group_name: data.get("groupName").cloned(),
            group_name_local: data.get("groupNameLocal").cloned(),
            office_site_name: data.get("officeSiteName").cloned(),
            annual_revenue: data.get("annualRevenue").and_then(|s| s.parse().ok()),
            num_employees: data.get("numEmployees").and_then(|s| s.parse().ok()),
            ticker_symbol: data.get("tickerSymbol").cloned(),
            group_comments: data.get("groupComments").cloned(),
            logo_image_url: data.get("logoImageUrl").cloned(),
        })
    }
}



/// PartyContactDetailByPurpose view-entity
/// 按用途分类的参与方联系详情视图
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartyContactDetailByPurpose {
    // PartyContactMech fields
    pub party_id: String,
    pub contact_mech_id: String,
    pub from_date: DateTime<Utc>,
    pub thru_date: Option<DateTime<Utc>>,
    pub role_type_id: Option<String>,
    pub allow_solicitation: Option<bool>,
    pub extension: Option<String>,
    pub verified: Option<bool>,
    pub comments: Option<String>,
    pub years_with_contact_mech: Option<f64>,
    pub months_with_contact_mech: Option<f64>,
    
    // PartyContactMechPurpose fields
    pub contact_mech_purpose_type_id: String,
    pub purpose_from_date: DateTime<Utc>,
    pub purpose_thru_date: Option<DateTime<Utc>>,
    
    // ContactMech fields
    pub contact_mech_type_id: String,
    pub info_string: Option<String>,
    
    // PostalAddress fields (optional)
    pub to_name: Option<String>,
    pub attn_name: Option<String>,
    pub address1: Option<String>,
    pub address2: Option<String>,
    pub house_number: Option<String>,
    pub house_number_ext: Option<String>,
    pub directions: Option<String>,
    pub city: Option<String>,
    pub city_geo_id: Option<String>,
    pub postal_code: Option<String>,
    pub postal_code_ext: Option<String>,
    pub country_geo_id: Option<String>,
    pub state_province_geo_id: Option<String>,
    pub county_geo_id: Option<String>,
    pub municipality_geo_id: Option<String>,
    pub postal_code_geo_id: Option<String>,
    pub geo_point_id: Option<String>,
    
    // TelecomNumber fields (optional)
    pub country_code: Option<String>,
    pub area_code: Option<String>,
    pub contact_number: Option<String>,
    pub ask_for_name: Option<String>,
    
    // Geo fields for state
    pub state_geo_id: Option<String>,
    pub state_geo_code: Option<String>,
    pub state_geo_name: Option<String>,
    pub state_abbreviation: Option<String>,
    
    // Geo fields for county (additional)
    pub county_geo_code: Option<String>,
    pub county_geo_name: Option<String>,
    pub county_abbreviation: Option<String>,
    
    // Geo fields for country (additional)
    pub country_geo_code: Option<String>,
    pub country_geo_name: Option<String>,
    pub country_abbreviation: Option<String>,
}

impl PartyContactDetailByPurpose {
    /// 从HashMap创建PartyContactDetailByPurpose实例
    pub fn from_data_row(data: &HashMap<String, String>) -> crate::Result<Self> {
        let party_id = data.get("partyId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing partyId".to_string())))?
            .clone();
        let contact_mech_id = data.get("contactMechId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing contactMechId".to_string())))?
            .clone();
        let from_date = data.get("fromDate")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing fromDate".to_string())))
            .and_then(|s| DateTime::parse_from_rfc3339(s)
                .map_err(|_| Box::new(crate::PartyModError::InvalidData("Invalid fromDate format".to_string()))))
            .map(|dt| dt.with_timezone(&Utc))?;
        let contact_mech_purpose_type_id = data.get("contactMechPurposeTypeId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing contactMechPurposeTypeId".to_string())))?
            .clone();
        let purpose_from_date = data.get("purposeFromDate")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing purposeFromDate".to_string())))
            .and_then(|s| DateTime::parse_from_rfc3339(s)
                .map_err(|_| Box::new(crate::PartyModError::InvalidData("Invalid purposeFromDate format".to_string()))))
            .map(|dt| dt.with_timezone(&Utc))?;
        let contact_mech_type_id = data.get("contactMechTypeId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing contactMechTypeId".to_string())))?
            .clone();

        Ok(Self {
            party_id,
            contact_mech_id,
            from_date,
            thru_date: data.get("thruDate")
                .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
                .map(|dt| dt.with_timezone(&Utc)),
            role_type_id: data.get("roleTypeId").cloned(),
            allow_solicitation: data.get("allowSolicitation").map(|s| s == "Y"),
            extension: data.get("extension").cloned(),
            verified: data.get("verified").map(|s| s == "Y"),
            comments: data.get("comments").cloned(),
            years_with_contact_mech: data.get("yearsWithContactMech").and_then(|s| s.parse().ok()),
            months_with_contact_mech: data.get("monthsWithContactMech").and_then(|s| s.parse().ok()),
            contact_mech_purpose_type_id,
            purpose_from_date,
            purpose_thru_date: data.get("purposeThruDate")
                .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
                .map(|dt| dt.with_timezone(&Utc)),
            contact_mech_type_id,
            info_string: data.get("infoString").cloned(),
            to_name: data.get("toName").cloned(),
            attn_name: data.get("attnName").cloned(),
            address1: data.get("address1").cloned(),
            address2: data.get("address2").cloned(),
            house_number: data.get("houseNumber").cloned(),
            house_number_ext: data.get("houseNumberExt").cloned(),
            directions: data.get("directions").cloned(),
            city: data.get("city").cloned(),
            city_geo_id: data.get("cityGeoId").cloned(),
            postal_code: data.get("postalCode").cloned(),
            postal_code_ext: data.get("postalCodeExt").cloned(),
            country_geo_id: data.get("countryGeoId").cloned(),
            state_province_geo_id: data.get("stateProvinceGeoId").cloned(),
            county_geo_id: data.get("countyGeoId").cloned(),
            municipality_geo_id: data.get("municipalityGeoId").cloned(),
            postal_code_geo_id: data.get("postalCodeGeoId").cloned(),
            geo_point_id: data.get("geoPointId").cloned(),
            country_code: data.get("countryCode").cloned(),
            area_code: data.get("areaCode").cloned(),
            contact_number: data.get("contactNumber").cloned(),
            ask_for_name: data.get("askForName").cloned(),
            state_geo_id: data.get("stateGeoId").cloned(),
            state_geo_code: data.get("stateGeoCode").cloned(),
            state_geo_name: data.get("stateGeoName").cloned(),
            state_abbreviation: data.get("stateAbbreviation").cloned(),
            county_geo_code: data.get("countyGeoCode").cloned(),
            county_geo_name: data.get("countyGeoName").cloned(),
            county_abbreviation: data.get("countyAbbreviation").cloned(),
            country_geo_code: data.get("countryGeoCode").cloned(),
            country_geo_name: data.get("countryGeoName").cloned(),
            country_abbreviation: data.get("countryAbbreviation").cloned(),
        })
    }
}

impl PartyAndPostalAddress {
    /// 从HashMap创建PartyAndPostalAddress实例
    pub fn from_data_row(data: &HashMap<String, String>) -> crate::Result<Self> {
        let party_id = data.get("partyId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing partyId".to_string())))?
            .clone();
        let party_type_id = data.get("partyTypeId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing partyTypeId".to_string())))?
            .clone();
        let contact_mech_id = data.get("contactMechId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing contactMechId".to_string())))?
            .clone();
        let contact_mech_type_id = data.get("contactMechTypeId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing contactMechTypeId".to_string())))?
            .clone();
        let from_date = data.get("fromDate")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing fromDate".to_string())))
            .and_then(|s| DateTime::parse_from_rfc3339(s)
                .map_err(|_| Box::new(crate::PartyModError::InvalidData("Invalid fromDate format".to_string()))))
            .map(|dt| dt.with_timezone(&Utc))?;

        Ok(Self {
            party_id,
            party_type_id,
            status_id: data.get("statusId").cloned(),
            contact_mech_id,
            from_date,
            thru_date: data.get("thruDate")
                .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
                .map(|dt| dt.with_timezone(&Utc)),
            comments: data.get("comments").cloned(),
            extension: data.get("extension").cloned(),
            allow_solicitation: data.get("allowSolicitation").map(|s| s == "Y"),
            contact_mech_type_id,
            info_string: data.get("infoString").cloned(),
            to_name: data.get("toName").cloned(),
            attn_name: data.get("attnName").cloned(),
            address1: data.get("address1").cloned(),
            address2: data.get("address2").cloned(),
            house_number: data.get("houseNumber").cloned(),
            house_number_ext: data.get("houseNumberExt").cloned(),
            directions: data.get("directions").cloned(),
            city: data.get("city").cloned(),
            city_geo_id: data.get("cityGeoId").cloned(),
            postal_code: data.get("postalCode").cloned(),
            postal_code_ext: data.get("postalCodeExt").cloned(),
            country_geo_id: data.get("countryGeoId").cloned(),
            state_province_geo_id: data.get("stateProvinceGeoId").cloned(),
            county_geo_id: data.get("countyGeoId").cloned(),
            municipality_geo_id: data.get("municipalityGeoId").cloned(),
            postal_code_geo_id: data.get("postalCodeGeoId").cloned(),
            geo_point_id: data.get("geoPointId").cloned(),
        })
    }
}

impl PartyAndPerson {
    /// 从HashMap创建PartyAndPerson实例
    pub fn from_data_row(data: &HashMap<String, String>) -> crate::Result<Self> {
        let party_id = data.get("partyId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing partyId".to_string())))?
            .clone();
        let party_type_id = data.get("partyTypeId")
            .ok_or_else(|| Box::new(crate::PartyModError::InvalidData("Missing partyTypeId".to_string())))?
            .clone();

        Ok(Self {
            party_id,
            party_type_id,
            external_id: data.get("externalId").cloned(),
            preferred_currency_uom_id: data.get("preferredCurrencyUomId").cloned(),
            description: data.get("description").cloned(),
            status_id: data.get("statusId").cloned(),
            created_date: data.get("createdDate")
                .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
                .map(|dt| dt.with_timezone(&Utc)),
            created_by_user_login: data.get("createdByUserLogin").cloned(),
            last_modified_date: data.get("lastModifiedDate")
                .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
                .map(|dt| dt.with_timezone(&Utc)),
            last_modified_by_user_login: data.get("lastModifiedByUserLogin").cloned(),
            data_source_id: data.get("dataSourceId").cloned(),
            is_unread: data.get("isUnread").map(|s| s == "Y"),
            salutation: data.get("salutation").cloned(),
            first_name: data.get("firstName").cloned(),
            middle_name: data.get("middleName").cloned(),
            last_name: data.get("lastName").cloned(),
            personal_title: data.get("personalTitle").cloned(),
            suffix: data.get("suffix").cloned(),
            nickname: data.get("nickname").cloned(),
            first_name_local: data.get("firstNameLocal").cloned(),
            middle_name_local: data.get("middleNameLocal").cloned(),
            last_name_local: data.get("lastNameLocal").cloned(),
            other_local: data.get("otherLocal").cloned(),
            member_id: data.get("memberId").cloned(),
            gender: data.get("gender").cloned(),
            birth_date: data.get("birthDate")
                .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
                .map(|dt| dt.with_timezone(&Utc)),
            deceased_date: data.get("deceasedDate")
                .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
                .map(|dt| dt.with_timezone(&Utc)),
            height: data.get("height").and_then(|s| s.parse().ok()),
            weight: data.get("weight").and_then(|s| s.parse().ok()),
            mothers_maiden_name: data.get("mothersMaidenName").cloned(),
            marital_status_enum_id: data.get("maritalStatusEnumId").cloned(),
            social_security_number: data.get("socialSecurityNumber").cloned(),
            passport_number: data.get("passportNumber").cloned(),
            passport_expire_date: data.get("passportExpireDate")
                .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
                .map(|dt| dt.with_timezone(&Utc)),
            total_years_work_experience: data.get("totalYearsWorkExperience").and_then(|s| s.parse().ok()),
            comments: data.get("comments").cloned(),
            employment_status_enum_id: data.get("employmentStatusEnumId").cloned(),
            residence_status_enum_id: data.get("residenceStatusEnumId").cloned(),
            occupation: data.get("occupation").cloned(),
            years_with_employer: data.get("yearsWithEmployer").and_then(|s| s.parse().ok()),
            months_with_employer: data.get("monthsWithEmployer").and_then(|s| s.parse().ok()),
            existing_customer: data.get("existingCustomer").map(|s| s == "Y"),
            card_id: data.get("cardId").cloned(),
        })
    }
}